package com.example.service;

import com.example.thread.ConfigThread;
import com.example.utils.DataChange;
import com.example.utils.Pong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class IndexServiceImpl implements IndexService, CommandLineRunner, DisposableBean {

    private final Map<String, ConfigThread> configThreadMap = new ConcurrentHashMap<>();
    private final long maxWaitTime = 60000L;
    Logger logger = LoggerFactory.getLogger(IndexServiceImpl.class);
    private ClusterService clusterService;
    @Resource
    private ApplicationContext applicationContext;

    @Value("${sitc.cluster.class}")
    private String clusterClass;

    /***
     * 响应心跳包
     * @param appKey
     * @param serverKey
     * @param timeStamp
     * @param request
     * @param response
     */
    @Override
    public void request(String appKey, String serverKey, long timeStamp, HttpServletRequest request, HttpServletResponse response) {
        String uuid = UUID.randomUUID().toString();
        ConfigThread configThread = new ConfigThread(maxWaitTime, appKey, serverKey, new Pong(uuid, timeStamp));
        this.configThreadMap.put(uuid, configThread);
        Object result = this.configThreadMap.get(uuid).getResponse();
        if (result != null) {
            ObjectOutputStream outputStream = null;
            try {
                outputStream = new ObjectOutputStream(response.getOutputStream());
                outputStream.writeObject(result);
                outputStream.flush();
            } catch (Exception e) {
                ConfigThread failThread = this.configThreadMap.remove(uuid);
                if (failThread != null) {
                    logger.warn("客户端地址: " + failThread.getServerKey() + " 响应失败，并已断开连接.");
                }
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        logger.warn("关闭输出流失败：" + e.getMessage());
                    }
                }
            }
        }
    }


    /***
     * 刷新配置信息, 此时应该集群内传递消息
     * @param dataChange
     */
    @Override
    public void refresh(DataChange dataChange) {
        for (String key : this.configThreadMap.keySet()) {
            ConfigThread configThread = this.configThreadMap.get(key);
            if (configThread.getAppKey().equalsIgnoreCase(configThread.getAppKey())) {
                configThread.setResponse(dataChange);
                this.configThreadMap.remove(key);
            }
        }
        this.clusterService.notifyToMessage(dataChange);
    }

    @Override
    public void onMessage(DataChange dataChange) {
        for (String key : this.configThreadMap.keySet()) {
            ConfigThread configThread = this.configThreadMap.get(key);
            if (configThread.getAppKey().equalsIgnoreCase(configThread.getAppKey())) {
                configThread.setResponse(dataChange);
                this.configThreadMap.remove(key);
            }
        }
    }

    @Override
    public void getServer() {
        for (String key : this.configThreadMap.keySet()) {
            ConfigThread configThread = this.configThreadMap.get(key);
            logger.warn("在线节点: " + configThread.getServerKey());
        }
    }

    @Override
    public void run(String... args) throws Exception {
        Class<?> logClassLoader = Class.forName(clusterClass);
        this.clusterService = (ClusterService) logClassLoader.newInstance();
        this.clusterService.start(applicationContext, this);
    }

    @Override
    public void destroy() throws Exception {
        this.clusterService.stop();
    }
}
